#include "Button.h"

#include <iostream>

using namespace Utils::GUI;

Control::Control()
	: parent(NULL), is_visible(true), has_mouse_over(false)
{
}

Control::~Control()
{
	if (parent != NULL)
		parent->removeChild(this);

	ControlChildren children_cpy;
	for(ControlChildren::iterator i = children_cpy.begin(); i != children_cpy.end(); ++i)
	{
		delete *i;
	}
}

Control::Control(const Control& org)
{
	*this = org;
}

Control& Control::operator=(const Control& rhs)
{
	if (&rhs == this)
		return *this;
	
	parent = rhs.parent;
	is_visible = rhs.is_visible;
	Shape = rhs.Shape;

	for(ControlChildren::const_iterator i = rhs.children.begin(); i != rhs.children.end(); ++i)
	{
		children.push_back((*i)->Clone());
	}
	return *this;
}

Control* Control::Clone()
{
	return new Control(*this);
}

bool Control::isWindow()
{
	return false;
}

sf::RenderWindow* Control::getWindow()
{
	return parent->getWindow();
}

void Control::addChild(Control* control)
{
	children.push_back(control);
	control->parent = this;
}

void Control::removeChild(Control* control)
{
	for(ControlChildren::iterator i = children.begin(); i != children.end(); ++i)
	{
		if (*i == control)
		{
			children.erase(i);
			break;
		}
	}
}

bool Control::isVisible()
{
	return is_visible;
}

void Control::show()
{
	is_visible = true;
}

void Control::hide()
{
	is_visible = false;
}

boost::signals::connection Control::doOnClick(const OnClick::slot_function_type& slot)
{
	return clickEvent.connect(slot);
}

boost::signals::connection Control::doOnMouseEnter(const OnMouseEnter::slot_function_type& slot)
{
	return mouseEnterEvent.connect(slot);
}

boost::signals::connection Control::doOnMouseLeave(const OnMouseLeave::slot_function_type& slot)
{
	return mouseLeaveEvent.connect(slot);
}

boost::signals::connection Control::doOnKeyPressedEvent(const OnKeyPressed::slot_function_type& slot)
{
	return keyPressedEvent.connect(slot);
}

void Control::Draw()
{
	Draw(false);
}

void Control::Draw(bool display)
{
	sf::RenderWindow* window = getWindow();
	Draw(display, window);
}

void Control::Draw(bool display, sf::RenderWindow* window)
{
	window->Draw(Shape);
	for(ControlChildren::iterator i = children.begin(); i != children.end(); ++i)
	{
		(*i)->Draw(false, window);
	}
	if (display)
		window->Display();
}

float Control::GetHeightF()
{
	float miny = 0, maxy = 0;
	int nvert = Shape.GetNbPoints();
	for (int i = 0; i < nvert; ++i)
	{
		float y = Shape.GetPointPosition(i).y;
		if (y < miny)
			miny = y;
		else if (y > maxy)
			maxy = y;
	}
	return maxy - miny;
}

int Control::GetHeight()
{
	return static_cast<int>(GetHeightF());
}

float Control::GetWidthF()
{
	float minx = 0, maxx = 0;
	int nvert = Shape.GetNbPoints();
	for (int i = 0; i < nvert; ++i)
	{
		float x = Shape.GetPointPosition(i).x;
		if (x < minx)
			minx = x;
		else if (x > maxx)
			maxx = x;
	}
	return maxx - minx;
}

int Control::GetWidth()
{
	return static_cast<int>(GetWidthF());
}

sf::Rect<int> Control::GetRect()
{
	return sf::Rect<int>(Shape.GetPosition().x, Shape.GetPosition().y, Shape.GetPosition().x + GetWidthF(), Shape.GetPosition().y + GetHeightF());
}

void Control::Move(int x, int y)
{
	Move(static_cast<float>(x), static_cast<float>(y));
}

void Control::Move(float x, float y)
{
	Shape.Move(x, y);
	for(ControlChildren::iterator i = children.begin(); i != children.end(); ++i)
	{
		(*i)->Move(x, y);
	}
}

void Control::MoveCenter()
{
	float x = parent->GetWidthF()/2 - GetWidthF()/2;
	float y = parent->GetHeightF()/2 - GetHeightF()/2;
	Move(x, y);
}

bool Control::Contains(int x, int y)
{
	return Contains(static_cast<float>(x), static_cast<float>(y));
}

bool Control::Contains(float x, float y)
{
	float testx = x - Shape.GetPosition().x, testy = y - Shape.GetPosition().y;
	int nvert = Shape.GetNbPoints();
	float *vertx = new float[nvert];
	float *verty = new float[nvert];
	for (int i = 0; i < nvert; ++i)
	{
		vertx[i] = Shape.GetPointPosition(i).x;
		verty[i] = Shape.GetPointPosition(i).y;
	}
	int i, j, c = 0;
	for (i = 0, j = nvert-1; i < nvert; j = i++) {
		if ( ((verty[i]>testy) != (verty[j]>testy)) &&
			(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
			c = !c;
	}
	delete vertx;
	delete verty;
	if (c)
		return true;
	else
		return false;
}

bool Control::ConditionalClick(int x, int y)
{
	if (isVisible() && Contains(x, y))
	{
		Click();
		for(ControlChildren::reverse_iterator i = children.rbegin(); i != children.rend(); ++i)
		{
			if ((*i)->ConditionalClick(x, y))
				break;
		}
		return true;
	}
	return false;
}

void Control::Click()
{
	clickEvent();
}

void Control::MouseEnter()
{
	has_mouse_over = true;
	mouseEnterEvent();
}

bool Control::ConditionalMouseEnter(int actual_x, int actual_y)
{
	bool entered = false;
	if (isVisible() && Contains(actual_x, actual_y))
	{
		if (!hasMouseOver())
		{
			MouseEnter();
			entered = true;
		}
		for(ControlChildren::reverse_iterator i = children.rbegin(); i != children.rend(); ++i)
		{
			if ((*i)->ConditionalMouseEnter(actual_x, actual_y))
				entered = true;
			if ((*i)->hasMouseOver())
				break;
		}
	}
	return entered;
}

void Control::MouseLeave()
{
	has_mouse_over = false;
	mouseLeaveEvent();
}

bool Control::ConditionalMouseLeave(int actual_x, int actual_y)
{
	bool left = false;
	if (isVisible())
	{
		if (!Contains(actual_x, actual_y) && hasMouseOver())
		{
			MouseLeave();
			left = true;
		}
		for(ControlChildren::reverse_iterator i = children.rbegin(); i != children.rend(); ++i)
		{
			if ((*i)->ConditionalMouseLeave(actual_x, actual_y))
			{
				left = true;
			}
			if ((*i)->isVisible() && (*i)->Contains(actual_x, actual_y))
			{
				ControlChildren::reverse_iterator j = i;
				++j;
				for(; j != children.rend(); ++j)
				{
					(*j)->ForceRecurisiveMouseLeave();
				}
				left = true;
				break;
			}
		}
	}
	return left;
}

void Control::ForceRecurisiveMouseLeave()
{
	if (isVisible() && hasMouseOver())
	{
		MouseLeave();
		for(ControlChildren::reverse_iterator i = children.rbegin(); i != children.rend(); ++i)
		{
			(*i)->ForceRecurisiveMouseLeave();
		}
	}
}

bool Control::hasMouseOver()
{
	return has_mouse_over;
}

bool Control::ConditionalKeyPressed(sf::Key::Code code)
{
	if (isVisible())
	{
		KeyPressed(code);
		for(ControlChildren::reverse_iterator i = children.rbegin(); i != children.rend(); ++i)
		{
			(*i)->ConditionalKeyPressed(code);
		}
		return true;
	}
	return false;
}

void Control::KeyPressed(sf::Key::Code code)
{
	keyPressedEvent(code);
}

